package com.shiehub.sdk.opengw.encrypt.SM2Util;

import java.math.BigInteger;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.math.ec.ECPoint;

/**
 * @Description: SM2
 * @Author: guoleijie@shie.com.cn
 * @Since:2016年9月13日 17:25:00
 */
public class Cipher {

  private int ct;
  private ECPoint p2;
  private SM3Digest sm3keybase;
  private SM3Digest sm3c3;
  private byte key[];
  private byte keyOff;

  /**
   * @Description:初始化方法
   * @ReturnType
   * @Since:2016年9月13日 17:25:00
   * @Author: guoleijie@shie.com.cn
   */
  public Cipher() {
    this.ct = 1;
    this.key = new byte[32];
    this.keyOff = 0;
  }

  /**
   * @Description:
   * @ReturnType
   * @Since:2016年9月13日 17:25:00
   * @Author: guoleijie@shie.com.cn
   */
  private void Reset() {
    this.sm3keybase = new SM3Digest();
    this.sm3c3 = new SM3Digest();

    byte p[] = Util.byteConvert32Bytes(p2.getX().toBigInteger());
    this.sm3keybase.update(p, 0, p.length);
    this.sm3c3.update(p, 0, p.length);

    p = Util.byteConvert32Bytes(p2.getY().toBigInteger());
    this.sm3keybase.update(p, 0, p.length);
    this.ct = 1;
    NextKey();
  }

  /**
   * @Description:
   * @ReturnType
   * @Since:2016年9月13日 17:25:00
   * @Author: guoleijie@shie.com.cn
   */
  private void NextKey() {
    SM3Digest sm3keycur = new SM3Digest(this.sm3keybase);
    sm3keycur.update((byte) (ct >> 24 & 0xff));
    sm3keycur.update((byte) (ct >> 16 & 0xff));
    sm3keycur.update((byte) (ct >> 8 & 0xff));
    sm3keycur.update((byte) (ct & 0xff));
    sm3keycur.doFinal(key, 0);
    this.keyOff = 0;
    this.ct++;
  }

  /**
   * @Description:
   * @ReturnType: ECPoint
   * @Since:2016年9月13日 17:25:00
   * @Author: guoleijie@shie.com.cn
   */
  public ECPoint Init_enc(SM2 sm2, ECPoint userKey) {
    AsymmetricCipherKeyPair key = sm2.ecc_key_pair_generator
        .generateKeyPair();
    ECPrivateKeyParameters ecpriv = (ECPrivateKeyParameters) key
        .getPrivate();
    ECPublicKeyParameters ecpub = (ECPublicKeyParameters) key.getPublic();
    BigInteger k = ecpriv.getD();
    ECPoint c1 = ecpub.getQ();
    this.p2 = userKey.multiply(k);
    Reset();
    return c1;
  }

  /**
   * @Description:
   * @ReturnType
   * @Since:2016年9月13日 17:25:00
   * @Author: guoleijie@shie.com.cn
   */
  void Encrypt(byte data[]) {
    this.sm3c3.update(data, 0, data.length);
    for (int i = 0; i < data.length; i++) {
      if (keyOff == key.length) {
        NextKey();
      }
      data[i] ^= key[keyOff++];
    }
  }

  /**
   * @Description:
   * @ReturnType
   * @Since:2016年9月13日 17:25:00
   * @Author: guoleijie@shie.com.cn
   */
  public void Init_dec(BigInteger userD, ECPoint c1) {
    this.p2 = c1.multiply(userD);
    Reset();
  }

  /**
   * @Description:
   * @ReturnType
   * @Since:2016年9月13日 17:25:00
   * @Author: guoleijie@shie.com.cn
   */
  public void Decrypt(byte data[]) {
    for (int i = 0; i < data.length; i++) {
      if (keyOff == key.length) {
        NextKey();
      }
      data[i] ^= key[keyOff++];
    }

    this.sm3c3.update(data, 0, data.length);
  }

  /**
   * @Description:
   * @ReturnType
   * @Since:2016年9月13日 17:25:00
   * @Author: guoleijie@shie.com.cn
   */
  public void Dofinal(byte c3[]) {
    byte p[] = Util.byteConvert32Bytes(p2.getY().toBigInteger());
    this.sm3c3.update(p, 0, p.length);
    this.sm3c3.doFinal(c3, 0);
    Reset();
  }
}
